package com.uwaterloo.interviewme.am;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import android.content.Context;

import com.uwaterloo.interviewme.am.udm.SQLiteUserDataManager;
import com.uwaterloo.interviewme.am.udm.UserDataManager;
import com.uwaterloo.interviewme.exception.ServerException;
import com.uwaterloo.interviewme.model.DetailInfo;
import com.uwaterloo.interviewme.model.Interview;
import com.uwaterloo.interviewme.requsthandler.RequestHandler;
import com.uwaterloo.interviewme.requsthandler.SocketRequestHandler;

/**
 * Facade to {@link UserDataManager} and {@link RequestHandler}.
 * 
 */
public class ApplicationManager {

	/** The instance. */
	private static ApplicationManager instance;

	/** The udm. */
	private UserDataManager udm;

	/** The res handler. */
	private RequestHandler resHandler;

	/** The ctx. */
	private Context ctx;

	/** The interviews. */
	private Collection<Interview> interviews;

	/** The user name. */
	private final String userName;

	/**
	 * Instantiates a new application manager.
	 * 
	 * @param userName
	 *            the user name
	 */
	private ApplicationManager(String userName) {
		this.userName = userName;
	}

	/**
	 * Create an instance of Application Manager.
	 * 
	 * @param userName
	 *            the user name
	 * @return the application manager
	 */
	public static ApplicationManager createInstance(String userName) {
		if (instance == null || !userName.equals(instance.getUserName())) {
			instance = new ApplicationManager(userName);
		}
		return instance;
	}

	/**
	 * Gets the single instance of ApplicationManager.
	 * 
	 * @return single instance of ApplicationManager
	 */
	public static ApplicationManager getInstance() {
		if (instance == null) {
			throw new RuntimeException("instance is not created");
		}
		return instance;
	}

	/**
	 * Login a user.
	 * 
	 * @param pwd
	 * @return true, if successful
	 * @throws ServerException 
	 */
	public boolean login(String pwd) throws ServerException {
		boolean isLogin = resHandler.login(userName, pwd);
		return isLogin;
	}

	/**
	 * Delete an interview.
	 * 
	 * @param rowID
	 * @return true, if successful
	 */
	public boolean deleteInterview(int rowID) {
		/*Get interview id from in memory cache*/
		Interview interview = getInterview(rowID);
		String interviewID = interview.getInterviewID();
		
		/*remove interview from in memory cache*/
		interviews.remove(interview);
		
		/*if in memory cache is empty, invalid it*/
		if (interviews.isEmpty()) interviews = null;
		
		/*remove interview from device cache*/
		return udm.deleteInterview(interviewID);
	}

	/**
	 * Update interview.
	 * 
	 * @param interview
	 * @return true, if successful
	 */
	public boolean updateInterview(Interview interview) {
		return udm.updateInterview(interview);
	}

	/**
	 * Load Interview List.
	 * 
	 * @return the collection
	 * @throws ServerException 
	 */
	public Collection<Interview> loadInterviewList() throws ServerException {
		/*In memory is empty*/
		if (interviews != null) {
			return interviews;
		} else {
			/*Make server request and update in memory cache*/
			interviews = resHandler.getAllInterview(userName);
			
			// Persist on device
			for (Interview itv : interviews) {
				udm.saveInterview(itv);
			}

			// Retrieve from device
			return udm.retriveAllInterview();
		}
	}

	/**
	 * Load a single interview.
	 * 
	 * @param rowID
	 * @return the interview
	 * @throws ServerException 
	 */
	public Interview loadInterview(int rowID) throws ServerException {
		/*Get interview ID off the in memory cache*/
		String interviewID = getInterview(rowID).getInterviewID();
		
		/*First check device cache*/
		Interview interview = udm.retriveInterview(interviewID);

		/*Cache missed*/
		if (interview.getDetailInfo() == null) {
			/*make server request*/
			DetailInfo detailInfo = resHandler.getOneInterview(userName, String.valueOf(interviewID));
			interview.setDetailInfo(detailInfo);
			
			/*Update local cache*/
			updateInterview(interview);
		}
		return interview;
	}

	/**
	 * Set Syncing interval.
	 * 
	 * @param interval
	 * @throws ServerException 
	 */
	public void setInterval(int interval) throws ServerException {
		/*Delegate to the request handler*/
		resHandler.setSyncInterval(userName, interval);
	}

	/**
	 * Sets the context. This method should be called by each view after get an instance of AM and
	 * before doing anything with the am.
	 * 
	 * This limitation is because that the local cache(UDM) needs to access on device sqlite which
	 * requires a context
	 * 
	 * @param ctx
	 */
	public void setContext(Context ctx) {
		this.ctx = ctx;
		this.udm = new SQLiteUserDataManager(ctx);
		if(resHandler == null) this.resHandler = new SocketRequestHandler(ctx);
	}

	/**
	 * Gets the interview.
	 * 
	 * @param rowID
	 *            the row id
	 * @return the interview
	 */
	private Interview getInterview(int rowID) {
		List<Interview> itv = new ArrayList<Interview>(interviews);
		return itv.get(rowID);
	}

	/**
	 * Gets the user name.
	 * 
	 * @return the user name
	 */
	private String getUserName() {
		return userName;
	}

	/**
	 * Export to google calendar. Currently not implemented
	 * 
	 * @return true, if successful
	 */
	public boolean exportToGoogleCal() {
		return false;
	}
}
